package com.tanhua.server.service;

import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.tanhua.commons.properties.OssProperties;
import com.tanhua.commons.templates.FaceTemplate;
import com.tanhua.commons.templates.OssTemplate;
import com.tanhua.domain.db.UserInfo;
import com.tanhua.domain.vo.UserInfoVo;

import com.tanhua.dubbo.api.UserInfoApi;
import com.tanhua.server.interceptor.UserHolder;
import com.tanhua.server.utils.GetAgeUtil;
import com.tanhua.server.utils.JwtUtils;
import com.alibaba.fastjson.JSON;
import com.tanhua.commons.exception.TanHuaException;
import com.tanhua.commons.templates.SmsTemplate;
import com.tanhua.domain.db.User;
import com.tanhua.domain.vo.ErrorResult;
import com.tanhua.dubbo.api.UserApi;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 用户管理业务层
 */
@Service
@Slf4j
public class UserService {
    @Reference
    private UserApi userApi;

    @Autowired
    private SmsTemplate smsTemplate;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private RedisTemplate redisTemplate;

    @Reference
    private UserInfoApi userInfoApi;

    @Autowired
    private FaceTemplate faceTemplate;

    @Autowired
    private OssTemplate ossTemplate;

    @Autowired
    private OssProperties ossProperties;

    @Value("${tanhua.redisValidateCodeKeyPrefix}")
    private String redisValidateCodeKeyPrefix;

    /**
     * StringRedisTemplate  key与value序列化方式都是String
     * RedisTemplate  key与value序列化方式都是jdk方式
     */
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 添加用户
     *
     * @param user
     */
    public void add(User user) {
        Long userId = userApi.add(user);
        System.out.println(userId);
    }

    /**
     * 通过手机号码查询
     *
     * @param phone
     * @return
     */
    public User findByMobile(String phone) {
        return userApi.findByMobile(phone);
    }

    /**
     * 发送验证码
     *
     * @param paramMap
     */
    public void sendValidateCode(Map<String, String> paramMap) {
        //1. 取出手机号码
        String phone = paramMap.get("phone");
        //2. 拼接验证码的key
        String key = redisValidateCodeKeyPrefix + phone;
        //3. 通过key获取redis中的验证码
        String codeInRedis = stringRedisTemplate.opsForValue().get(key);
        log.info("codeInRedis: {},{}", phone, codeInRedis);
        //4. 存在, 报错：验证码未失效
        if (StringUtils.isNotEmpty(codeInRedis)) {
            // 有值
            //System.out.println("报错：验证码未失效");
            throw new TanHuaException(ErrorResult.duplicate());
        }
        //5. 不存在
        //6.    生成验证码
        String validateCode = "123456";//RandomStringUtils.randomNumeric(6);
        //7.    发送验证码
        //Map<String, String> resultMap = smsTemplate.sendValidateCode(phone, validateCode);
        //if(null != resultMap){
        //    //System.out.println("发送验证码失败了" +  resultMap.get(SmsTemplate.SMSRESPONSE_MESSAGE));
        //    log.error("发送验证码失败了! {}, {}", resultMap.get(SmsTemplate.SMSRESPONSE_CODE),resultMap.get(SmsTemplate.SMSRESPONSE_MESSAGE));
        //    throw new TanHuaException(ErrorResult.fail());
        //}
        //8.    存入redis，设置有效期为10分钟。
        stringRedisTemplate.opsForValue().set(key, validateCode, 10, TimeUnit.MINUTES);
    }

    /**
     * 登录注册 - 验证码校验
     *
     * @param paramMap
     * @return
     */
    public Map<String, Object> loginVerification(Map<String, String> paramMap) {
        //1.验证码校验
        //1.1 取出手机号码与验证码
        String phone = paramMap.get("phone");
        // 验证码
        String verificationCode = paramMap.get("verificationCode");
        //1.2 拼接redis中的key
        String key = redisValidateCodeKeyPrefix + phone;
        //1.3 从redis取中验证码
        String codeInRedis = stringRedisTemplate.opsForValue().get(key);
        log.info("验证码校验：{},{},{}", phone, verificationCode, codeInRedis);
        //1.4 redis中的验证码失效，
        if (StringUtils.isEmpty(codeInRedis)) {
            //为null，报错：验证码已失效，请重新获取
            throw new TanHuaException(ErrorResult.loginError());
        }
        //1.5 存在，比较前端与redis中的验证码是否相同
        if (!StringUtils.equals(codeInRedis, verificationCode)) {
            //1.6 不相同则报错，验证码错误
            throw new TanHuaException(ErrorResult.validateCodeError());
        }
        //1.7 相同，删除redis中的key。防止重复提交，导致频繁的查询数据库
        stringRedisTemplate.delete(key);

        //2.是否已存在用户
        boolean isNew = false;
        //  通过手机号码查询用户信息
        User loginUser = userApi.findByMobile(phone);
        //2.1 不存在则注册为新用户
        if (null == loginUser) {
            //注册为新用户
            loginUser = new User();
            loginUser.setMobile(phone);
            // 手机号码后6位，并进行md5加密
            loginUser.setPassword(DigestUtils.md5Hex(phone.substring(5)));
            Long userId = userApi.add(loginUser);
            loginUser.setId(userId);
            // 新注册
            isNew = true;
        }
        //3. 签发token

        String token = jwtUtils.createJWT(phone, loginUser.getId());
        //4. 把token做为key,用户信息为value存入redis备用，当用户下次再请求时，会带token，从redis中来获取用户信息，如果取不到说明用户没登录，或失效
        //                              存入redis时要设置有效期
        String loginUserJsonString = JSON.toJSONString(loginUser);
        // 7天有效期
        stringRedisTemplate.opsForValue().set("TOKEN_" + token, loginUserJsonString, 7, TimeUnit.DAYS);
        //构建返回Map
        Map<String, Object> resultMap = new HashMap<String, Object>();
        resultMap.put("token", token);
        resultMap.put("isNew", isNew);
        return resultMap;
    }

    public static void main(String[] args) {
        String phone = "12345678912";

        System.out.println(DigestUtils.md5Hex(phone.substring(5)));
    }

    /**
     * 通过token获取登入信息
     *
     * @param token
     * @return
     */
    public User getUserByToken(String token) {
        //通过存入redis的key和token获取redis中的用户登入信息
        String tokenKey = "TOKEN_" + token;
        //查看登入状态，太长时间未使用则报错
        String userJsonString = stringRedisTemplate.opsForValue().get(tokenKey);
        if (null == userJsonString) {
            throw new TanHuaException("登入超时，请重新登入");
        }
        //延长登入有效期，重新设置有效期
        stringRedisTemplate.expire(tokenKey, 7, TimeUnit.DAYS);
        //转成java对象返回
        User loginUser = JSON.parseObject(userJsonString, User.class);
        return loginUser;
    }

    /**
     * 完善个人信息
     *
     * @param userInfoVo
     * @param token
     */
    public void loginReginfo(UserInfoVo userInfoVo, String token) {
        //通过token获取用户id
        User loginUser = getLoginUserByToken(token);
        UserInfo userInfo = new UserInfo();
        //把UserInfoVo转成实体类
        BeanUtils.copyProperties(userInfoVo, userInfo);
        //设置登入id
        userInfo.setId(loginUser.getId());
        //设置年龄
        userInfo.setAge(GetAgeUtil.getAge(userInfoVo.getBirthday()));
        //调用Api储存对象
        userInfoApi.add(userInfo);
    }

    /**
     * 完善个人信息，上传头像
     *
     * @param headPhoto
     * @param token
     */
    public void uploadAvatar(MultipartFile headPhoto, String token) {
        //通过token获取登入的id
        Long loginUserId = getUserByToken(token).getId();
        //调用人脸识别
        try {
            //没有通过则报错
            if (!faceTemplate.detect(headPhoto.getBytes())) {
                throw new TanHuaException(ErrorResult.faceError());
            }
            //通过获取头像地址
            String picUrl = ossTemplate.upload(headPhoto.getOriginalFilename(), headPhoto.getInputStream());
            //构建pojo
            UserInfo pojo = new UserInfo();
            pojo.setId(loginUserId);
            pojo.setAvatar(picUrl);
            //调用api更新用户信息
            userInfoApi.update(pojo);
        } catch (IOException e) {
            log.error("上传头像失败", e);
            throw new TanHuaException("上传头像失败!");
        }

    }

    /**
     * 通过token获取redis中存入的登录用户信息
     *
     * @param token
     * @return
     */
    private User getLoginUserByToken(String token) {
        //1.1 拼接key,必须与存入时的key要一致
        String key = "TOKEN_" + token;
        //1.2 从redis中取出
        String loginUserJsonString = stringRedisTemplate.opsForValue().get(key);
        //1.3 redis中没有值， 报错，重新登录
        if (StringUtils.isEmpty(loginUserJsonString)) {
            //报错，重新登录
            throw new TanHuaException("登录超时，请重新登录!");
        }
        //1.4 有值，把json字符串转成java对象User
        return JSON.parseObject(loginUserJsonString, User.class);
    }

    /**
     * 查询用户信息
     * @param userID
     * @param huanxinID
     * @param token
     * @return
     */
    public UserInfoVo getUserInfo(Long userID, Long huanxinID, String token) {
        //1. 获取登录用户id,校验token是否有效
        User loginUser = UserHolder.getUser();
        //2. 选择要查询的id
        Long userId = loginUser.getId();
        if (null != userID) {
            userId = userID;
        }
        if (null != huanxinID) {
            userId = huanxinID;
        }
        //3. 调用api查询用户信息
        UserInfo userInfo = userInfoApi.findById(userId);
        //4. 转成vo返回
        UserInfoVo vo = new UserInfoVo();
        // 复制属性
        BeanUtils.copyProperties(userInfo, vo);
        // 年龄
        vo.setAge(userInfo.getAge().toString());
        return vo;
    }

    /**
     * 更新用户资料
     *
     * @param token
     * @param vo
     */

    public void updateUserInfo(String token, UserInfoVo vo) {
        //根据token获取用户id
        Long loginUserId =UserHolder.getUserId();
        //把USerInfoVo转换成java对象
        UserInfo userInfo = new UserInfo();
        BeanUtils.copyProperties(vo, userInfo);
        //设置年龄
        userInfo.setAge(GetAgeUtil.getAge(vo.getAge()));
        //设置更新id，为传入的id
        userInfo.setId(loginUserId);
        //调用Api更新
        userInfoApi.update(userInfo);

    }

    /**
     * 更新用户头像
     *
     * @param headPhoto
     * @param token
     */
    public void updatePhoto(MultipartFile headPhoto, String token) {
        //根据token获取用户id
        Long loginUserId = UserHolder.getUserId();
        UserInfo user = userInfoApi.findById(loginUserId);
        String avatar = user.getAvatar();
        //调用人脸识别
        try {
            //没有通过则报错
            if (faceTemplate.detect(headPhoto.getBytes())) {
                throw new TanHuaException(ErrorResult.faceError());
            }
            //通过获取头像地址
            String picUrl = ossTemplate.upload(headPhoto.getOriginalFilename(), headPhoto.getInputStream());
            //构建pojo
            UserInfo pojo = new UserInfo();
            pojo.setId(loginUserId);
            pojo.setAvatar(picUrl);
            //调用api更新用户信息
            userInfoApi.update(pojo);
            //调用api删除头像
            deleteFile(avatar);

        } catch (IOException e) {
            log.error("上传头像失败", e);
            throw new TanHuaException("上传头像失败!");
        }

    }
    /**
     * 删除头像
     */
    public void deleteFile(String url) {
        // Endpoint以杭州为例，其它Region请按实际情况填写。
        String endpoint = ossProperties.getEndpoint();
        // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录 https://ram.console.aliyun.com 创建RAM账号。
        String accessKeyId = ossProperties.getAccessKeyId();
        String accessKeySecret = ossProperties.getAccessKeySecret();
        String bucketName = ossProperties.getBucketName();
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
        // 删除文件。如需删除文件夹，请将ObjectName设置为对应的文件夹名称。如果文件夹非空，则需要将文件夹下的所有object删除后才能删除该文件夹。
        ossClient.deleteObject(bucketName, url.replace(ossProperties.getUrl() + "/", ""));
        // 关闭OSSClient。
        ossClient.shutdown();
    }


}